import { Server } from '@modelcontextprotocol/sdk/server/index.js';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
import { ListToolsRequestSchema, CallToolRequestSchema } from '@modelcontextprotocol/sdk/types.js';
import { fetch } from 'undici';
import express from 'express';
import { AMAP_KEY, AMAP_BASE_URL, TIMEOUT_MS, CACHE_TTL_BASE_MS, CACHE_TTL_ALL_MS } from './config.js';

// 简单内存缓存（按 city|extensions 维度缓存）
const cache = new Map();

function getCache(key) {
  const hit = cache.get(key);
  if (!hit) return null;
  if (Date.now() > hit.expireAt) {
    cache.delete(key);
    return null;
  }
  return hit.value;
}

function setCache(key, value, ttlMs) {
  cache.set(key, { value, expireAt: Date.now() + ttlMs });
}

function toISO(t) {
  if (!t) return null;
  const d = new Date(t.replace(/-/g, '/'));
  return isNaN(d.getTime()) ? t : d.toISOString();
}

function normalizeBase(resp) {
  const live = Array.isArray(resp?.lives) ? resp.lives[0] : null;
  if (!live) return null;
  return {
    type: 'realtime',
    city: live.city,
    adcode: live.adcode,
    weather: live.weather,
    temperature: live.temperature != null ? Number(live.temperature) : null,
    windDirection: live.winddirection,
    windPower: live.windpower,
    humidity: live.humidity != null ? Number(live.humidity) : null,
    reportTime: toISO(live.reporttime),
  };
}

function normalizeAll(resp) {
  const f = Array.isArray(resp?.forecasts) ? resp.forecasts[0] : null;
  if (!f) return null;
  const casts = Array.isArray(f.casts) ? f.casts : [];
  return {
    type: 'forecast',
    city: f.city,
    adcode: f.adcode,
    reportTime: toISO(f.reporttime),
    forecasts: casts.map(c => ({
      date: c.date,
      week: c.week != null ? Number(c.week) : null,
      day: {
        weather: c.dayweather,
        temp: c.daytemp != null ? Number(c.daytemp) : null,
        windDirection: c.daywind,
        windPower: c.daypower,
      },
      night: {
        weather: c.nightweather,
        temp: c.nighttemp != null ? Number(c.nighttemp) : null,
        windDirection: c.nightwind,
        windPower: c.nightpower,
      },
    })),
  };
}

async function callAmap({ city, extensions }) {
  if (!AMAP_KEY) {
    const err = new Error('缺少 AMAP_KEY 配置');
    err.code = 'CONFIG_MISSING';
    throw err;
  }

  const url = new URL('/v3/weather/weatherInfo', AMAP_BASE_URL);
  url.searchParams.set('key', AMAP_KEY);
  url.searchParams.set('city', city);
  url.searchParams.set('extensions', extensions);
  url.searchParams.set('output', 'JSON');

  const ac = new AbortController();
  const timer = setTimeout(() => ac.abort(), TIMEOUT_MS);
  try {
    const res = await fetch(url, { signal: ac.signal });
    const text = await res.text();
    let json;
    try { json = JSON.parse(text); } catch (_) {
      const err = new Error('上游返回非 JSON');
      err.code = 'UPSTREAM_BAD_JSON';
      err.upstream = { statusCode: res.status, body: text };
      throw err;
    }
    if (json.status !== '1') {
      const err = new Error(json.info || '上游返回错误');
      err.code = 'UPSTREAM_ERROR';
      err.upstream = { status: json.status, infocode: json.infocode, info: json.info };
      throw err;
    }
    return json;
  } finally {
    clearTimeout(timer);
  }
}

const inputSchema = {
  type: 'object',
  properties: {
    city: { type: 'string', description: '城市名称（中文）或 adcode，如 “青岛” 或 “370200”' },
    extensions: { type: 'string', enum: ['base', 'all'], default: 'base', description: 'base=实时天气，all=预报' },
  },
  required: ['city'],
};

const server = new Server(
  { name: 'mcp-weather', version: '1.0.0' },
  { capabilities: { tools: {} } }
);

server.setRequestHandler(ListToolsRequestSchema, async () => ({
  tools: [
    {
      name: 'weather.get',
      description: '查询高德天气。支持中文城市名或 adcode，extensions=base|all',
      inputSchema,
    },
  ],
}));

server.setRequestHandler(CallToolRequestSchema, async (req) => {
  const { name, arguments: args } = req.params;
  if (name !== 'weather.get') {
    return { content: [{ type: 'text', text: JSON.stringify({ error: 'Unknown tool' }) }] };
  }

  const city = (args?.city ?? '').toString().trim();
  const extensions = (args?.extensions ?? 'base').toString().trim();
  if (!city) {
    return { content: [{ type: 'text', text: JSON.stringify({ error: 'city 为必填' }) }] };
  }
  if (!['base', 'all'].includes(extensions)) {
    return { content: [{ type: 'text', text: JSON.stringify({ error: 'extensions 仅支持 base 或 all' }) }] };
  }

  const cacheKey = `${city}|${extensions}`;
  const ttl = extensions === 'base' ? CACHE_TTL_BASE_MS : CACHE_TTL_ALL_MS;
  const cached = getCache(cacheKey);
  if (cached) {
    return { content: [{ type: 'text', text: JSON.stringify({ provider: 'amap', query: { city, extensions }, data: cached, raw: null, meta: { cached: true } }) }] };
  }

  try {
    const upstream = await callAmap({ city, extensions });
    const data = extensions === 'base' ? normalizeBase(upstream) : normalizeAll(upstream);
    if (!data) {
      return { content: [{ type: 'text', text: JSON.stringify({ error: '未获取到数据', provider: 'amap' }) }] };
    }
    setCache(cacheKey, data, ttl);
    return {
      content: [
        { type: 'text', text: JSON.stringify({ provider: 'amap', query: { city, extensions }, data, raw: upstream, meta: { cached: false } }) },
      ],
    };
  } catch (e) {
    const errObj = {
      code: e.code || 'INTERNAL_ERROR',
      message: e.message || '未知错误',
      provider: 'amap',
      upstream: e.upstream,
    };
    return { content: [{ type: 'text', text: JSON.stringify({ error: errObj }) }] };
  }
});

const transport = new StdioServerTransport();
await server.connect(transport);


// 可选 HTTP 测试端点（不影响 stdio），通过设置 HTTP_PORT 开启
const httpPort = process.env.HTTP_PORT;
if (httpPort) {
  const app = express();
  app.get('/weather', async (req, res) => {
    try {
      const city = (req.query.city || '').toString();
      const extensions = (req.query.extensions || 'base').toString();
      if (!city) return res.status(400).json({ error: 'city 为必填' });
      if (!['base', 'all'].includes(extensions)) return res.status(400).json({ error: 'extensions 仅支持 base 或 all' });
      const cacheKey = `${city}|${extensions}`;
      const ttl = extensions === 'base' ? CACHE_TTL_BASE_MS : CACHE_TTL_ALL_MS;
      const cached = getCache(cacheKey);
      if (cached) return res.json({ provider: 'amap', query: { city, extensions }, data: cached, raw: null, meta: { cached: true } });
      const upstream = await callAmap({ city, extensions });
      const data = extensions === 'base' ? normalizeBase(upstream) : normalizeAll(upstream);
      if (!data) return res.status(404).json({ error: '未获取到数据', provider: 'amap' });
      setCache(cacheKey, data, ttl);
      return res.json({ provider: 'amap', query: { city, extensions }, data, raw: upstream, meta: { cached: false } });
    } catch (e) {
      return res.status(500).json({ error: e.message || '服务器错误', provider: 'amap', upstream: e.upstream });
    }
  });
  app.listen(Number(httpPort), () => {
    console.log(`[mcp-weather] HTTP test server listening on http://localhost:${httpPort}`);
  });
}


